# -*- coding:UTF-8 -*-
import os
import re

from infrastructure.constant.file_status_enum import FileStatusEnum
from infrastructure.utils.file_util import make_file, delete_file


class File:
    name = ""

    def __init__(self, relative_path, base, is_on_remote=True, is_on_local=False, size=0, is_folder=False, remote_time=None, parent=None):
        """
        文件实例
        :param relative_path: 相对路径
        :param base: 当前系统所定义的物理路径的前缀路径
        :param is_on_remote: 是否在云上
        :param is_on_local: 是否在本地
        :param size: 大小
        :param is_folder: 是否是目录
        :param remote_time: 云端文件最后更新时间
        """
        self.is_on_remote = is_on_remote
        self.is_on_local = is_on_local
        self.base = base
        self.relative_path = self.relative_path__(relative_path)
        self.size = size
        self.is_folder = is_folder
        self.remote_time = remote_time
        self.parent:File = parent
        self.children = dict()

    def relative_path__(self, relative_path:str):
        """
        规则化相对路径的格式，前后不带分隔符，且路径中的分隔符为os.sep
        :param relative_path:
        :return:
        """
        if not relative_path:
            return ""
        else:
            if self.base in relative_path:
                relative_path = os.path.relpath(relative_path, self.base)
            relative_path = re.sub("^[/|\\\\]*", "", relative_path)
            relative_path = re.sub("[/|\\\\]*$", "", relative_path)
            relative_path, n = re.subn("[\\\\]+", "/", relative_path)
            return relative_path

    def add_child(self, file):
        if not file or not isinstance(file, self.__class__):
            return False
        else:
            if self.relative_path not in file.relative_path:
                return False
            if self.relative_path == file.relative_path:
                self.is_on_remote = self.is_on_remote if self.is_on_remote else file.is_on_remote
                self.is_on_local = self.is_on_local if self.is_on_local else file.is_on_local
                self.size = self.size if self.size > 0 else file.size
                return True
            if self.is_descendant(file):
                if self.is_child(file):
                    file_o = self.children.get(file.relative_path, None)
                    if not file_o:
                        file.parent = self
                        self.children[file.relative_path] = file
                        return True
                else:
                    for f in self.children.values():
                        if f.add_child(file):
                            return True
        return False

    def download__(self):
        raise NotImplementedError("download__ 函数需要被子类实现")

    def download(self):
        """
        下载文件
        :return:
        """
        file_path = os.path.join(self.base, self.relative_path)
        if self.is_folder:
            if self.update_status(recursive=True) != FileStatusEnum.remote_is_newer:
                return
            else:
                for name, item in self:
                    item.download()
        else:
            if self.update_status(recursive=False) != FileStatusEnum.remote_is_newer:
                return
            else:
                if os.path.exists(file_path):
                    os.remove(file_path)
                else:
                    if self.is_folder:
                        os.makedirs(file_path, mode=0o755, exist_ok=True)
                    else:
                        os.makedirs(os.path.split(file_path)[0], mode=0o755, exist_ok=True)
                self.download__()

    def delete__(self):
        raise NotImplementedError("delete__ 函数需要被子类实现")

    # def delete(self, file):
    #     """
    #     删除后代 文件
    #     :return:
    #     """
    #     if self.is_child(file):
    #         if file.is_folder:
    #             for name, item in file:
    #                 file.delete(item)
    #         if file.is_on_remote:
    #             file.delete__()
    #         if file.is_on_local:
    #             delete_file(os.path.join(self.base, self.relative_path))
    #         del self.children[file.relative_path]
    #     elif self.is_descendant(file):
    #         for name, item in self:
    #             if item.is_descendant(file):
    #                 item.delete(file)

    def delete(self):
        """
        删除当前文件
        :return:
        """
        file_path = self.base + os.sep + self.relative_path if self.relative_path is not None and len(self.relative_path) > 0 else self.base
        if self.is_folder:
            for item in self.children:
                item.delete()
            if self.is_on_local:
                delete_file(file_path)
        else:
            if self.is_on_remote:
                self.delete__()
            if self.is_on_local:
                delete_file(file_path)

    def update__(self):
        raise NotImplementedError("update__ 函数需要被子类实现")

    def update(self):
        file_path = os.path.join(self.base, self.relative_path)
        if self.is_folder:
            for name, item in self:
                item.update()
        else:
            status = self.update_status(recursive=False)
            if status == FileStatusEnum.local_is_newer:
                if os.stat(file_path).st_size > 0:
                    self.update__()
            elif status == FileStatusEnum.remote_is_newer:
                self.download__()

    def add__(self):
        raise NotImplementedError("add__ 函数需要被子类实现")

    def upload(self):
        """
        上传文件
        :return:
        """
        if self.is_on_remote:
            return False
        if not self.is_on_local:
            return False
        if self.is_folder and self.get_child_size() > 0:
            for name, item in self:
                return item.update()
        else:
            if os.stat(os.path.join(self.base, self.relative_path)).st_size > 0:
                self.add__()

    def upload_or_update(self):
        """
        更新或上传文件
        :return:
        """
        if self.is_on_local and not self.is_on_remote:
            self.upload()
        else:
            self.update()

    def new(self, name: str, is_folder=True):
        if name and len(name) > 0:
            relative_path = os.path.join(self.relative_path, name)
            path = os.path.join(self.base, relative_path)
            if is_folder:
                file = self.__class__(relative_path=relative_path, base=self.base, is_on_remote=False, is_on_local=True, size=0, is_folder=True)
            else:
                file = self.__class__(relative_path=relative_path, base=self.base, is_on_remote=False, is_on_local=True,
                                      size=0, is_folder=False)
                make_file(path)
                if os.stat(path).st_size > 0:
                    file.add__()
            self.add_child(file)

    def refresh(self):
        raise NotImplementedError("__refresh 函数需要被子类实现")

    def get_name(self) -> str:
        """
        获取当前文件的文件名，带后缀
        :return:
        """
        return os.path.split(self.relative_path)[1]

    def get_path(self):
        """
        获取文件绝对路径
        :return:
        """
        return os.path.join(self.base, self.relative_path)

    def get_ext(self) -> str:
        """
        获取当前文件扩展名，带"."
        :return:
        """
        if not self.is_folder:
            return os.path.splitext(self.relative_path)[1]
        else:
            return ""

    def get_size(self):
        """
        迭代计算当前文件及其子文件的大小总和
        :return:
        """
        count = 0
        if not self.is_folder:
            count = os.path.getSize(self.base + os.sep + self.relative_path) if self.is_on_local else self.size
        else:
            if self.children and len(self.children) > 0:
                for child in self.children.values():
                    count += child.file_size()
        return count

    def __eq__(self, other) -> bool:
        if self.__class__ != other.__class__:
            return False
        if self.relative_path != other.relative_path:
            return False
        if self.is_folder == other.is_folder:
            return False

    def is_descendant(self, other) -> bool:
        """
        判断other是否是当前文件的子孙文件
        :param other: File或文件路径
        :return:
        """
        other_path = None
        if other:
            if isinstance(other, str):
                other_path = self.relative_path__(other)
            elif isinstance(other, File):
                other_path = other.relative_path
            if self.relative_path in other_path:
                return True
        return False

    def is_child(self, other) -> bool:
        """
        判断other是否是当前文件的儿女文件
        :param other: File或文件路径
        :return:
        """
        other_path = None
        if other:
            if isinstance(other, str):
                other_path = self.relative_path__(other)
            elif isinstance(other, File):
                other_path = other.relative_path
            if self.relative_path == os.path.split(other_path)[0]:
                return True
        return False

    def contain_file(self, other) -> bool:
        """
        判断当前文件是否等于或包含给定文件
        :param other: 路径字符串或File及子类实例
        :return:
        """
        if not other:
            return False
        if isinstance(other, str):
            rel_path = self.relative_path__(other)
            if self.relative_path == rel_path:
                return True
            if self.relative_path not in rel_path:
                return False
            if not self.children or len(self.children) == 0:
                return False
            for child in self.children.values():
                is_contain = child.contain_file(rel_path)
                if is_contain:
                    return is_contain
        elif isinstance(other, File):
            if self == other:
                return True
            else:
                if not self.children or len(self.children) == 0:
                    return False
                for child in self.children.values():
                    is_contain = child.contain_file(other)
                    if is_contain:
                        return is_contain
        return False

    def add_files(self):
        """
        把本目录下边的没有加载进来的物理文件全部加载进来
        :return:
        """
        if self.is_folder:
            dir = self.base + os.sep + self.relative_path if self.relative_path is not None and self.relative_path != '' else self.base
            if not os.path.exists(dir):
                return
            files = os.listdir(dir)
            if files is not None and len(files) > 0:
                for f in files:
                    if f.startswith("."):
                        continue
                    f_rel_path = self.relative_path__(os.path.join(self.relative_path, f))
                    f_path = os.path.join(self.base, f_rel_path)
                    file = None
                    if f_rel_path not in self.children.keys():
                        file = self.__class__(relative_path=f_rel_path, base=self.base, is_on_remote=False,
                                              is_on_local=True,
                                              size=os.path.getsize(f_path) if not os.path.isdir(f_path) else 0,
                                              is_folder=os.path.isdir(f_path), parent=self)
                        self.children[f_rel_path] = file
                    else:
                        file = self.children[f_rel_path]
                        file.is_on_local = True
                    if os.path.isdir(f_path):
                        file.add_files()

    def update_status(self, recursive=False) -> int:
        """
        判断文件更新状态
        :param recursive: 是否递归判断
        :return: 1：本地版本晚于远程， 或远程不存在；0：本地和远程版本相同；-1：远程晚于本地, 或本地不存在
        """
        if self.is_on_remote and not self.is_on_local:
            return FileStatusEnum.remote_is_newer
        elif not self.is_on_remote and self.is_on_local:
            return FileStatusEnum.local_is_newer
        else:
            if self.is_folder:
                if len(self.children) > 0:
                    for child in self.children.values():
                        return child.update_status(recursive=recursive)
                else:
                    return FileStatusEnum.local_is_remote
            elif not self.is_folder:
                self.update_remote_time__()
                local_update_time = os.stat(os.path.join(self.base, self.relative_path)).st_mtime
                if local_update_time > self.remote_time.timestamp():
                    return FileStatusEnum.local_is_newer
                elif local_update_time == self.remote_time.timestamp():
                    return FileStatusEnum.local_is_remote
                else:
                    return FileStatusEnum.remote_is_newer

    def update_remote_time__(self):
        """
        更新远程最新提交时间
        :return:
        """
        raise NotImplementedError("__new 函数需要被子类实现")

    def get_file(self, path:str):
        path = self.relative_path__(path)
        if path == self.relative_path:
            return self
        if not self.children or len(self.children) == 0 or not self.contain_file(path):
            return None
        for child in self.children.values():
            file = child.get_file(path)
            if file:
                return file
        return None

    def open(self):
        """
        调用系统安装的软件打开文件
        :return:
        """
        import platform
        import subprocess
        if self.is_folder:
            pass
        else:
            file_path = os.path.join(self.base, self.relative_path)
            if not self.is_on_local:
                self.download()
            sys = platform.system()
            if sys == "Windows":
                os.startfile(file_path)
            elif sys == "Linux":
                subprocess.call(["xdg-open",file_path])
            elif sys == "Darwin": # 苹果
                subprocess.call(["open", file_path])
            else:
                pass

    def __iter__(self):
        if self.children and len(self.children) > 0:
            for name, item in self.children.items():
                yield name, item
        else:
            return None, None

    def get_child_size(self):
        return len(self.children)

    @classmethod
    def get_subclasses(cls):
        return cls.__subclasses__()

    def search(self, s):
        """
        检索文件
        :param s:
        :return:
        """
        file_list = []
        if s is None or len(s) == 0:
            return file_list
        if s in self.get_name():
            file_list.append(self)
        if self.children is not None and len(self.children) > 0:
            for name, child in self.children.items():
                file_list.extend(child.search(s))
        return file_list